package cn.edu.qfnu.oj.entity;

import cn.edu.qfnu.oj.entity.enums.Difficulty;
import com.google.common.base.Objects;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Entity
@DynamicUpdate
@DynamicInsert
public class Problem implements Serializable {

    private static final long serialVersionUID = -3233560291856016962L;
    private Integer id;  //问题ID
    private Long submitCount; //提交总数
    private Long submitStudentCount; //提交学生总数
    private Long ACStudentCount; //通过学生总数
    private Long ACCount; //总通过数
    private Long WACount; //总答案错误数
    private Long otherCount; //总其他错误数

    private String title;  // 标题
    private String content; // 内容
    private String inputDesc; // 输入描述
    private String outputDesc; // 输出描述
    private String sampleInput; // 输入样例
    private String sampleOutput; // 输出样例
    private String inputPath; // 输入路径
    private String outputPath; // 输出路径
    private String classify; // 类型
    private LocalDateTime createTime; // 创建时间
    private LocalDateTime modifyTime; //修改时间
    private Difficulty difficulty; // 难度
    private String hint; // 暗示
    private String sources; // 来源
    private Integer memoryLimit; // 内存限制
    private Integer timeLimit; // 时间限制

    private Boolean isACM; //是否是ACM题目
    private Boolean isTemp; //是否是临时题目
    private Teacher teacher; //题目与老师是多对一关系
    private List<Submit> submits = new ArrayList<>(); // 题目与提交记录是一对多关系
    private Set<Task> tasks = new HashSet<>();
    private Set<Contest> contests = new HashSet<>();

    public Problem() {}

    public Problem(String title, String content,
                   String inputDesc, String outputDesc, String sampleInput, String sampleOutput,
                   String classify, LocalDateTime createTime, Difficulty difficulty, String hint, String sources,
                   Integer memoryLimit, Integer timeLimit, Boolean isACM, Boolean isTemp) {
        this.title = title;
        this.content = content;
        this.inputDesc = inputDesc;
        this.outputDesc = outputDesc;
        this.sampleInput = sampleInput;
        this.sampleOutput = sampleOutput;
        this.classify = classify;
        this.createTime = createTime;
        this.difficulty = difficulty;
        this.hint = hint;
        this.sources = sources;
        this.memoryLimit = memoryLimit;
        this.timeLimit = timeLimit;
        this.submitCount = 0L;
        this.submitStudentCount = 0L;
        this.ACStudentCount = 0L;
        this.ACCount = 0L;
        this.WACount = 0L;
        this.otherCount = 0L;
        this.isACM = isACM;
        this.isTemp = isTemp;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "submit_count")
    public Long getSubmitCount() {
        return submitCount;
    }

    public void setSubmitCount(Long submitCount) {
        this.submitCount = submitCount;
    }

    @Column(name = "submit_student_count")
    public Long getSubmitStudentCount() {
        return submitStudentCount;
    }

    public void setSubmitStudentCount(Long submitStudentCount) {
        this.submitStudentCount = submitStudentCount;
    }

    @Column(name = "AC_student_count")
    public Long getACStudentCount() {
        return ACStudentCount;
    }

    public void setACStudentCount(Long ACStudentCount) {
        this.ACStudentCount = ACStudentCount;
    }

    @Column(name = "AC_count")
    public Long getACCount() {
        return ACCount;
    }

    public void setACCount(Long ACCount) {
        this.ACCount = ACCount;
    }

    @Column(name = "WA_count")
    public Long getWACount() {
        return WACount;
    }

    public void setWACount(Long WACount) {
        this.WACount = WACount;
    }

    @Column(name = "other_count")
    public Long getOtherCount() {
        return otherCount;
    }

    public void setOtherCount(Long otherCount) {
        this.otherCount = otherCount;
    }

    @Column(length = 16)
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Column(length = 128)
    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Column(name = "in_description", length = 64)
    public String getInputDesc() {
        return inputDesc;
    }

    public void setInputDesc(String inputDesc) {
        this.inputDesc = inputDesc;
    }

    @Column(name = "out_description", length = 64)
    public String getOutputDesc() {
        return outputDesc;
    }

    public void setOutputDesc(String outputDesc) {
        this.outputDesc = outputDesc;
    }

    @Column(name = "sample_input", length = 32)
    public String getSampleInput() {
        return sampleInput;
    }

    public void setSampleInput(String sampleInput) {
        this.sampleInput = sampleInput;
    }

    @Column(name = "sample_output", length = 32)
    public String getSampleOutput() {
        return sampleOutput;
    }

    public void setSampleOutput(String sampleOutput) {
        this.sampleOutput = sampleOutput;
    }

    @Column(name = "input_path", length = 32)
    public String getInputPath() {
        return inputPath;
    }

    public void setInputPath(String inputPath) {
        this.inputPath = inputPath;
    }

    @Column(name = "output_path", length = 32)
    public String getOutputPath() {
        return outputPath;
    }

    public void setOutputPath(String outputPath) {
        this.outputPath = outputPath;
    }

    @Column(length = 32)
    public String getClassify() {
        return classify;
    }

    public void setClassify(String classify) {
        this.classify = classify;
    }

    @Column(name = "create_time")
    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    @Column(name = "modify_time")
    public LocalDateTime getModifyTime() {
        return modifyTime;
    }

    public void setModifyTime(LocalDateTime modifyTime) {
        this.modifyTime = modifyTime;
    }

    @Column
    @Enumerated(value = EnumType.STRING)
    public Difficulty getDifficulty() {
        return difficulty;
    }

    public void setDifficulty(Difficulty difficulty) {
        this.difficulty = difficulty;
    }

    @Column(length = 64)
    public String getHint() {
        return hint;
    }

    public void setHint(String hint) {
        this.hint = hint;
    }

    @Column(length = 64)
    public String getSources() {
        return sources;
    }

    public void setSources(String sources) {
        this.sources = sources;
    }

    @Column(name = "memory_limit")
    public Integer getMemoryLimit() {
        return memoryLimit;
    }

    public void setMemoryLimit(Integer memoryLimit) {
        this.memoryLimit = memoryLimit;
    }

    @Column(name = "time_limit")
    public Integer getTimeLimit() {
        return timeLimit;
    }

    public void setTimeLimit(Integer timeLimit) {
        this.timeLimit = timeLimit;
    }

    @Column(name = "is_ACM")
    public Boolean getACM() {
        return isACM;
    }

    public void setACM(Boolean ACM) {
        isACM = ACM;
    }

    @Column(name = "is_temp")
    public Boolean getTemp() {
        return isTemp;
    }

    public void setTemp(Boolean temp) {
        isTemp = temp;
    }

    // 一个老师可以创建多个问题，这多个问题都隶属于一个老师
    // 老师和问题之间是一对多关系
    @ManyToOne // 维护老师与问题的一对多关系
    @JoinColumn(name = "teacher_id", referencedColumnName = "teacher_id")
    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    // 一个问题可以有多个提交记录，但是这多个提交记录只能同时隶属于一个问题
    // 所以问题与提交记录是一对多关系
    @OneToMany(mappedBy = "problem") // 问题与提交记录的一对多关系交给提交记录维护
    public List<Submit> getSubmits() {
        return submits;
    }

    public void setSubmits(List<Submit> submits) {
        this.submits = submits;
    }

    @ManyToMany(mappedBy = "problems")
    public Set<Task> getTasks() {
        return tasks;
    }

    public void setTasks(Set<Task> tasks) {
        this.tasks = tasks;
    }

    @ManyToMany(mappedBy = "problems")
    public Set<Contest> getContests() {
        return contests;
    }

    public void setContests(Set<Contest> contests) {
        this.contests = contests;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Problem problem = (Problem) o;
        return Objects.equal(id, problem.id) &&
                Objects.equal(submitCount, problem.submitCount) &&
                Objects.equal(submitStudentCount, problem.submitStudentCount) &&
                Objects.equal(ACStudentCount, problem.ACStudentCount) &&
                Objects.equal(ACCount, problem.ACCount) &&
                Objects.equal(WACount, problem.WACount) &&
                Objects.equal(otherCount, problem.otherCount) &&
                Objects.equal(title, problem.title) &&
                Objects.equal(content, problem.content) &&
                Objects.equal(inputDesc, problem.inputDesc) &&
                Objects.equal(outputDesc, problem.outputDesc) &&
                Objects.equal(sampleInput, problem.sampleInput) &&
                Objects.equal(sampleOutput, problem.sampleOutput) &&
                Objects.equal(inputPath, problem.inputPath) &&
                Objects.equal(outputPath, problem.outputPath) &&
                Objects.equal(classify, problem.classify) &&
                Objects.equal(createTime, problem.createTime) &&
                Objects.equal(modifyTime, problem.modifyTime) &&
                difficulty == problem.difficulty &&
                Objects.equal(hint, problem.hint) &&
                Objects.equal(sources, problem.sources) &&
                Objects.equal(memoryLimit, problem.memoryLimit) &&
                Objects.equal(timeLimit, problem.timeLimit) &&
                Objects.equal(isACM, problem.isACM) &&
                Objects.equal(isTemp, problem.isTemp) &&
                Objects.equal(teacher, problem.teacher) &&
                Objects.equal(submits, problem.submits) &&
                Objects.equal(tasks, problem.tasks) &&
                Objects.equal(contests, problem.contests);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id, submitCount, submitStudentCount, ACStudentCount,
                ACCount, WACount, otherCount, title, content, inputDesc, outputDesc,
                sampleInput, sampleOutput, inputPath, outputPath, classify, createTime,
                modifyTime, difficulty, hint, sources, memoryLimit, timeLimit, isACM, isTemp,
                teacher, submits, tasks, contests);
    }

    @Override
    public String toString() {
        return "Problem{" +
                "id=" + id +
                ", submitCount=" + submitCount +
                ", submitStudentCount=" + submitStudentCount +
                ", ACStudentCount=" + ACStudentCount +
                ", ACCount=" + ACCount +
                ", WACount=" + WACount +
                ", otherCount=" + otherCount +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", inputDesc='" + inputDesc + '\'' +
                ", outputDesc='" + outputDesc + '\'' +
                ", sampleInput='" + sampleInput + '\'' +
                ", sampleOutput='" + sampleOutput + '\'' +
                ", inputPath='" + inputPath + '\'' +
                ", outputPath='" + outputPath + '\'' +
                ", classify='" + classify + '\'' +
                ", createTime=" + createTime +
                ", modifyTime=" + modifyTime +
                ", difficulty=" + difficulty +
                ", hint='" + hint + '\'' +
                ", sources='" + sources + '\'' +
                ", memoryLimit=" + memoryLimit +
                ", timeLimit=" + timeLimit +
                ", isACM=" + isACM +
                ", isTemp=" + isTemp +
                ", teacher=" + teacher +
                ", submits=" + submits +
                ", tasks=" + tasks +
                ", contests=" + contests +
                '}';
    }
}
